use axum::http::header;
use axum::response::IntoResponse;
use axum::routing::post;
use axum::{Json, Router, extract, http, response};
use rand::Rng;
use serde::Deserialize;
use serde_json::{Value, json};

use crate::router::api::i_router::IRouter;

#[derive(Deserialize, Debug)]
struct User {
    email: String,
    password: String,
}

// 共享状态结构体
#[derive(Debug)]
pub struct AxumResp {}

impl IRouter for AxumResp {
    fn init_router(&self, router: Router) -> Router {
        let gg = Router::new()
            .route("/string", post(AxumResp::string))
            .route("/plain_text", post(AxumResp::plain_text))
            .route("/json", post(AxumResp::json))
            .route("/bytes", post(AxumResp::bytes))
            .route("/status", post(AxumResp::status))
            .route("/html", post(AxumResp::html))
            .route("/headers", post(AxumResp::headers))
            .route("/array_headers", post(AxumResp::array_headers))
            .route("/impl_trait", post(AxumResp::impl_trait))
            .route("/with_status", post(AxumResp::with_status))
            .route("/with_headers", post(AxumResp::with_headers))
            .route("/with_array_headers", post(AxumResp::with_array_headers))
            .route(
                "/with_array_headers_custom",
                post(AxumResp::with_array_headers_custom),
            )
            .route(
                "/with_status_and_array_headers",
                post(AxumResp::with_status_and_array_headers),
            )
            .route(
                "/with_status_extensions",
                post(AxumResp::with_status_extensions),
            )
            .route("/into_response", post(AxumResp::with_into_response));

        router.nest("/resp", gg)
    }
}

impl AxumResp {
    pub fn new() -> AxumResp {
        AxumResp {}
    }

    async fn string() -> String {
        "str".to_string()
    }
    async fn plain_text() -> &'static str {
        "foo"
    }
    async fn bytes() -> Vec<u8> {
        vec![1, 2, 3, 4]
    }
    async fn json() -> Json<Value> {
        Json(json!({ "data": 42 }))
    }
    async fn status() -> http::StatusCode {
        http::StatusCode::MOVED_PERMANENTLY
    }
    // `Html` will get a `text/html` content-type
    async fn html() -> response::Html<&'static str> {
        response::Html("<p>Hello, World!</p>")
    }
    async fn headers() -> header::HeaderMap {
        let mut headers = header::HeaderMap::new();
        headers.insert(header::SERVER, "axum-server".parse().unwrap());
        headers
    }
    async fn array_headers() -> [(header::HeaderName, &'static str); 2] {
        [
            (header::SERVER, "axum-server"),
            (header::CONTENT_TYPE, "text/plain"),
        ]
    }
    // Use `impl IntoResponse` to avoid writing the whole type
    async fn impl_trait() -> impl response::IntoResponse {
        [
            (header::SERVER, "axum"),
            (header::CONTENT_TYPE, "text/plain"),
        ]
    }
    //============================
    // `(StatusCode, impl IntoResponse)` will override the status code of the response
    async fn with_status(uri: http::Uri) -> (http::StatusCode, String) {
        (
            http::StatusCode::NOT_FOUND,
            format!("Not Found: {}", uri.path()),
        )
    }

    // `(HeaderMap, impl IntoResponse)` to add additional headers
    async fn with_headers() -> impl response::IntoResponse {
        let mut headers = http::HeaderMap::new();
        headers.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap());
        headers.insert(header::ACCEPT, "axum".parse().unwrap());
        (headers, "foo")
    }

    // Or an array of tuples to more easily build the headers
    async fn with_array_headers() -> impl response::IntoResponse {
        ([(header::CONTENT_TYPE, "text/plain")], "foo")
    }

    // Use string keys for custom headers
    async fn with_array_headers_custom() -> impl response::IntoResponse {
        ([("x-custom", "custom")], "foo")
    }

    // `(StatusCode, headers, impl response::IntoResponse)` to set status and add headers
    // `headers` can be either a `HeaderMap` or an array of tuples
    async fn with_status_and_array_headers() -> impl response::IntoResponse {
        (
            http::StatusCode::NOT_FOUND,
            [(header::CONTENT_TYPE, "text/plain")],
            "foo",
        )
    }

    // `(Extension<_>, impl response::IntoResponse)` to set response extensions
    async fn with_status_extensions() -> impl response::IntoResponse {
        (extract::Extension(Foo("foo")), "foo")
    }

    async fn with_into_response() -> impl response::IntoResponse {
        let mut rng = rand::thread_rng();
        let rand_nu = rng.gen_range(0..10);
        println!("{:?}", rand_nu,);

        match rand_nu % 4 {
            0 => {
                return (
                    http::StatusCode::INTERNAL_SERVER_ERROR,
                    "Something went wrong...",
                )
                    .into_response();
            }
            1 => {
                //Create a new Redirect that uses a 308 Permanent Redirect status code.
                return response::Redirect::permanent("/resp/string").into_response();
            }
            2 => {
                // Create a new Redirect that uses a 303 See Other status code.
                return response::Redirect::to("https://baidu.com").into_response();
            }
            3 => {
                // Create a new Redirect that uses a 307 Temporary Redirect status code.
                return response::Redirect::temporary("https://baidu.com").into_response();
            }
            _ => {
                return (http::StatusCode::INTERNAL_SERVER_ERROR, "default").into_response();
            }
        }
    }
}

#[derive(Clone)]
struct Foo(&'static str);
